package com.dpstudio.ymprestlful.utils.scanner;

import com.dpstudio.ymprestlful.annotation.YmpHttpMethodAnnotation;
import com.dpstudio.ymprestlful.beans.ControllerVo;
import com.dpstudio.ymprestlful.beans.HttpMethod;
import com.dpstudio.ymprestlful.beans.Request;
import com.dpstudio.ymprestlful.utils.CommentUtils;
import com.dpstudio.ymprestlful.utils.RestUtil;
//import com.github.javaparser.JavaParser;
//import com.github.javaparser.ast.CompilationUnit;
//import com.github.javaparser.ast.NodeList;
//import com.github.javaparser.ast.body.MethodDeclaration;
//import com.github.javaparser.ast.body.Parameter;
//import com.github.javaparser.ast.body.TypeDeclaration;
import com.intellij.lang.jvm.JvmParameter;
import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex;
import com.intellij.psi.search.GlobalSearchScope;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.InputStream;
import java.util.*;

/**
 * @Author: mengxiang.
 * @Date: 2020/6/22.
 * @Time: 11:37 上午.
 * @Description:
 */
public class YmpHelper {

    @NotNull
    public static List<Request> getSpringRequestByModule(@NotNull Project project, @NotNull Module module) {
        List<Request> moduleList = new ArrayList<>(0);

        List<ControllerVo> controllerVoList = getAllControllerClass(project, module);

        for (ControllerVo controllerVo : controllerVoList) {
            PsiClass controllerClass = controllerVo.getPsiClass();
            String packageInfoPath = controllerVo.getPackagePath();
            List<Request> parentRequests = new ArrayList<>(0);
            List<Request> childrenRequests = new ArrayList<>();
            PsiAnnotation psiAnnotation = RestUtil.getClassAnnotation(
                    controllerClass,
                    YmpHttpMethodAnnotation.REQUEST_MAPPING.getQualifiedName()
            );
            if (psiAnnotation != null) {
                parentRequests = getRequests(packageInfoPath, psiAnnotation, null);
            }

            PsiMethod[] psiMethods = controllerClass.getMethods();
            for (PsiMethod psiMethod : psiMethods) {
                childrenRequests.addAll(getRequests(psiMethod));
            }
            parentRequests.forEach(parentRequest -> childrenRequests.forEach(childrenRequest -> {
                Request request = childrenRequest.copyWithParent(parentRequest);
                moduleList.add(request);
            }));
        }
        return moduleList;
    }


    /**
     * 获取所有的控制器类
     *
     * @param project project
     * @param module  module
     * @return Collection<PsiClass>
     */
    @NotNull
    private static List<ControllerVo> getAllControllerClass(@NotNull Project project, @NotNull Module module) {

        List<ControllerVo> controllerVoList = new ArrayList<>();

        GlobalSearchScope moduleScope = RestUtil.getModuleScope(module);
        Collection<PsiAnnotation> pathList = JavaAnnotationIndex.getInstance().get(
                Control.RequestMapping.getName(),
                project,
                moduleScope
        );
        Collection<PsiAnnotation> packageInfoList = JavaAnnotationIndex.getInstance().get(
                Control.RequestMapping.getName(),
                project,
                moduleScope
        );

        List<PsiAnnotation> packageInfoNewList = new ArrayList<>();
        for (PsiAnnotation packageInfo : packageInfoList) {
            String packInfoPath = packageInfo.getParent().getContainingFile().getVirtualFile().getPath();
            File file = new File(packInfoPath);
            String fileName = file.getName();
            if (Objects.equals("package-info.java", fileName) || Objects.equals("package-info.class", fileName)) {
                packageInfoNewList.add(packageInfo);
            }
        }
        for (PsiAnnotation psiAnnotation : pathList) {
            PsiModifierList psiModifierList = (PsiModifierList) psiAnnotation.getParent();
            PsiElement psiElement = psiModifierList.getParent();
            if (!(psiElement instanceof PsiClass)) {
                continue;
            }
            ControllerVo controllerVo = new ControllerVo();
            PsiClass psiClass = (PsiClass) psiElement;
            String controllerPath = psiAnnotation.getParent().getContainingFile().getVirtualFile().getPath();
            controllerPath = controllerPath.replaceAll("\\\\","/");
            controllerPath = controllerPath.substring(0, controllerPath.lastIndexOf("/"));
            String packagePath = "";
            for (PsiAnnotation packageInfo : packageInfoNewList) {
                String packInfoPath = packageInfo.getParent().getContainingFile().getVirtualFile().getPath();
                packInfoPath = packInfoPath.replaceAll("\\\\","/");
                packInfoPath = packInfoPath.substring(0, packInfoPath.lastIndexOf("/"));
                if (Objects.equals(controllerPath,packInfoPath)) {
                    List<JvmAnnotationAttribute> packageAttrs = packageInfo.getAttributes();
                    if (!packageAttrs.isEmpty()) {
                        Object value = RestUtil.getAttributeValue(packageAttrs.get(0).getAttributeValue());
                        if (value != null) {
                            packagePath = (String) value;
                        }
                        break;
                    }

                }
            }
            controllerVo.setPackagePath(packagePath);
            controllerVo.setPsiClass(psiClass);
            controllerVoList.add(controllerVo);
        }
        return controllerVoList;
    }

    /**
     * 获取注解中的参数，生成RequestBean
     *
     * @param annotation annotation
     * @return list
     * @see YmpHelper#getRequests(PsiMethod)
     */
    @NotNull
    private static List<Request> getRequests(String packageInfoPath, @NotNull PsiAnnotation annotation, @Nullable PsiMethod psiMethod) {
        YmpHttpMethodAnnotation spring = YmpHttpMethodAnnotation.getByQualifiedName(
                annotation.getQualifiedName()
        );
        if (spring == null) {
            return Collections.emptyList();
        }
        Set<HttpMethod> methods = new HashSet<>();
        methods.add(spring.getMethod());
        List<String> paths = new ArrayList<>();

        // 是否为隐式的path（未定义value或者path）
        boolean hasImplicitPath = true;
        List<JvmAnnotationAttribute> attributes = annotation.getAttributes();
        for (JvmAnnotationAttribute attribute : attributes) {
            String name = attribute.getAttributeName();

            if (methods.contains(HttpMethod.REQUEST) && "method".equals(name)) {
                // method可能为数组
                Object value = RestUtil.getAttributeValue(attribute.getAttributeValue());
                if (value instanceof String) {
                    methods.add(HttpMethod.valueOf((String) value));
                } else if (value instanceof List) {
                    //noinspection unchecked,rawtypes
                    List<String> list = (List) value;
                    for (String item : list) {
                        if (item != null) {
                            item = item.substring(item.lastIndexOf(".") + 1);
                            methods.add(HttpMethod.valueOf(item));
                        }
                    }
                }
            }

            boolean flag = false;
            for (String path : new String[]{"value", "path"}) {
                if (path.equals(name)) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                continue;
            }
            Object value = RestUtil.getAttributeValue(attribute.getAttributeValue());
            if (value instanceof String) {
                paths.add(((String) value));
            } else if (value instanceof List) {
                //noinspection unchecked,rawtypes
                List<Object> list = (List) value;
                list.forEach(item -> paths.add((String) item));
            }
            hasImplicitPath = false;
        }
        if (hasImplicitPath) {
            if (psiMethod != null) {
                paths.add("/");
            }
        }

        List<String> pathList = new ArrayList<>();
        for (String path : paths) {
            if (StringUtils.isNotBlank(packageInfoPath)) {
                pathList.add(packageInfoPath.concat("/").concat(path));
            } else {
                pathList.add(path);
            }

        }
        List<Request> requests = new ArrayList<>(pathList.size());
        if (psiMethod != null && RestUtil.scanListenerTitle(psiMethod.getProject(), psiMethod.getResolveScope())) {
//            String comment = getComment(psiMethod);
            String comment = "";
            pathList.forEach(path -> {
                for (HttpMethod method : methods) {
                    if (method.equals(HttpMethod.REQUEST) && methods.size() > 1) {
                        continue;
                    }
                    requests.add(new Request(
                            !method.equals(HttpMethod.REQUEST) ? method : HttpMethod.GET,
                            path,
                            psiMethod,
                            comment
                    ));

                }
            });
        } else {
            pathList.forEach(path -> {
                for (HttpMethod method : methods) {
                    if (method.equals(HttpMethod.REQUEST) && methods.size() > 1) {
                        continue;
                    }
                    requests.add(new Request(
                            !method.equals(HttpMethod.REQUEST) ? method : HttpMethod.GET,
                            path,
                            psiMethod
                    ));

                }
            });
        }

        return requests;
    }


//    private static String getComment(PsiMethod psiMethod) {
//        String comment = "";
//        try (InputStream in = psiMethod.getParent().getContainingFile().getVirtualFile().getInputStream()) {
//            Optional<CompilationUnit> optional = new JavaParser().parse(in).getResult();
//            CompilationUnit cu = optional.get();
//            String psiClassName = ((PsiClass) psiMethod.getParent()).getName();
//            TypeDeclaration typeDeclaration = null;
//            for (TypeDeclaration declaration : cu.getTypes()) {
//                if (declaration.getNameAsString().equals(psiClassName)) {
//                    typeDeclaration = declaration;
//                    break;
//                }
//            }
//            if (typeDeclaration == null) {
//                return comment;
//            }
//            List<MethodDeclaration> list = typeDeclaration.getMethodsByName(psiMethod.getName());
//            MethodDeclaration m = list.get(0);
//            if (list.size() > 1) {
//                for (MethodDeclaration methodDeclaration : list) {
//                    NodeList<Parameter> parameters = methodDeclaration.getParameters();
//                    JvmParameter[] jvmParameters = psiMethod.getParameters();
//                    if (parameters.size() == jvmParameters.length) {
//                        boolean flag = false;
//                        for (int i = 0; i < parameters.size(); i++) {
//                            if (!parameters.get(i).getType().asString().equals(((PsiType) psiMethod.getParameters()[i].getType()).getPresentableText())) {
//                                flag = true;
//                                break;
//                            }
//                        }
//                        if (!flag) {
//                            m = methodDeclaration;
//                            break;
//                        }
//                    }
//                }
//            }
//
//            List<String> comments = CommentUtils.asCommentList(org.apache.commons.lang3.StringUtils.defaultIfBlank(m.getComment().get().getContent(), ""));
//            if (!comments.isEmpty()) {
//                comment = comments.get(0);
//            }
//        } catch (Exception e) {
////            System.out.println(e.getMessage());
//        }
//        return comment;
//    }

    /**
     * 获取方法中的参数请求，生成RequestBean
     *
     * @param method Psi方法
     * @return list
     */
    @NotNull
    private static List<Request> getRequests(@NotNull PsiMethod method) {
        List<Request> requests = new ArrayList<>();
        for (PsiAnnotation annotation : RestUtil.getMethodAnnotations(method)) {
            requests.addAll(getRequests(null, annotation, method));
        }

        return requests;
    }

    enum Control {

        /**
         * <p>@Controller</p>
         */
        Controller("Controller"),
        RequestMapping("RequestMapping");


        private final String name;

        Control(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }
}
